package asteroids.model;

import java.lang.Math;

import be.kuleuven.cs.som.annotate.*;
import asteroids.CollisionListener;

/**
 * A class of ships with a position and a velocity in space and how they interact with each other.
 * @invar	A bullet always has a valid source
 * 			this.getSource() != null
 * @invar	...
 * 			| density = 7.8*10^12
 * 
 * @version 2.0
 * 
 * @author Thijs Peirelinck & Wouter Parys
 *
 */
public class Bullet extends FlyingObject {
	
	private final static double density = 7.8*Math.pow(10,12);
	private Ship source;
	private boolean hasHitBorder;

	/**
	 * 
	 * @param positionX
	 * @param positionY
	 * @param velocityX
	 * @param velocityY
	 * @param radius
	 * @param source
	 * 
	 * @effect	...
	 * 			|super(positionX,positionY,velocityX,velocityY,4/3*PI*radius^3*density,0)
	 * @effect	...
	 * 			|setRadius(radius)
	 * @post	...
	 * 			|this.hasHitBorder() == false
	 * @post	...
	 * 			|this.getSource() == source	
	 */
	public Bullet(double positionX, double positionY, double velocityX, double velocityY, double radius, Ship source) {
		super(positionX, positionY, velocityX, velocityY, (4/3)*Math.PI*Math.pow(radius, 3)*density, 0);
		setRadius(radius);
		this.hasHitBorder = false;
		this.source = source;
	}
	
	@Immutable @Raw @Basic
	public double getDensity() {
		return density;
	}
	
	@Basic @Raw
	public Ship getSource() {
		return source;
	}
	
	@Basic @Raw
	public void setSource(Ship source) {
		this.source = source;
	}
	
	@Basic @Raw
	public void hitBorder() {
		hasHitBorder = true;
	}
	
	@Basic @Raw
	public boolean hasHitBorder() {
		return hasHitBorder;
	}
	
	/**
	 * Collide a Bullet with another flyingObject.
	 * 
	 * @param	flyingObject
	 * 			The flyingObject that will collide with this bullet.
	 * @param	collisionListener
	 * 			The interface that takes care of a potential collision in the GUI.
	 * @effect	...
	 * 			| if not (this.getSource() == flyingObject)
	 * 			|	then 
	 * 			|		if (flyingObject instanceof Asteroid)
	 * 			|			then flyingObject.collide(this, collisionListener)
	 * 			|		else 
	 * 			|			super.collide(flyingObject, collisionListener)
	 * 			|			collisionListener.objectCollision(this, flyingObject, this.getCollisionPosition(flyingObject)[0], this.getCollisionPosition(flyingObject)[1])
	 *@throws	IllegalArgumentException
	 *			when the world of this ship isn't equal to the world of the flyingObject
	 *			| this.getWorld() != flyingObject.getWorld()
	 *@throws	IllegalArgumentException
	 *			when this or the flyingObject doesn't have a world
	 *			| (!this.hasWorld()) || (!flyingObject.hasWorld())
	 *@throws	NullPointerException
	 *			when flyingObject is the null-reference
	 *			| flyingObject == null
	 *@throws	IllegalStateException
	 * 			when this object is terminated
	 * 			| isTerminated()
	 *
	 */
	@Override
	public void collide(FlyingObject flyingObject, CollisionListener collisionListener) throws IllegalStateException, IllegalArgumentException, NullPointerException {
		if(isTerminated()) { throw new IllegalStateException(); }
		if(flyingObject.getWorld() != this.getWorld() || !this.hasWorld() || !flyingObject.hasWorld()) { throw new IllegalArgumentException();} 
		if (this.getSource() != flyingObject){
			if (flyingObject instanceof Asteroid) {
				((Asteroid)flyingObject).collide(this, collisionListener);
			}
			else {
				super.collide(flyingObject, collisionListener);
				collisionListener.objectCollision(this, flyingObject, this.getCollisionPosition(flyingObject)[0], this.getCollisionPosition(flyingObject)[1]);				
			}
		}
		
	}
	
	/**
	 * Bounce bullet off the wall.
	 * 
	 * @effect	...
	 * 			| if (hasHitBorder == true)
	 * 			|	then this.terminate()
	 * @effect	...
	 * 			| if (hasHitBorder == false)
	 * 			|	then super.bounceOffWall()
	 * 			|		 this.hasHitBorder() = true
	 * @throws	IllegalStateException
	 * 			when this object is terminated
	 * 			| isTerminated()
	 */
	@Override
	public void bounceOffWall() throws IllegalStateException {
		if(isTerminated()) { throw new IllegalStateException(); }
		if (hasHitBorder == true) {
			this.terminate();
		}
		
		else {
			super.bounceOffWall();
			this.hasHitBorder = true;
		}
	}
	
	/**
	 * terminates a bullet
	 * @post	this.getSource == null
	 * @effect 	this.getSource.removeBullet(this)
	 * @effect	super.terminate
	 */
	@Override
	public void terminate() {
		this.getSource().removeBullet(this);
		this.setSource(null);
		super.terminate();
	}
		
	
}
